SAS语言与数据管理

您所在的位置:网站首页 sas 时间转字符 SAS语言与数据管理

SAS语言与数据管理

2023-10-08 21:34| 来源: 网络整理| 查看: 265

SAS语言与数据管理 SAS语言构成 SAS语句 SAS表达式 SAS程序规则 SAS用作一般高级语言 赋值语句 输出语句 分支结构 循环结构 数组 函数 SAS/IML矩阵功能简介 SAS语言的数据管理功能 SAS数据步的运行机制 用INPUT语句输入数据 读入外部数据 数据集的复制与修改 用SET和OUTPUT语句拆分数据集 数据集的纵向合并 数据集的横向合并 用UPDATE语句更新数据集 用PROC SQL管理数据 练习

SAS系统强大的数据管理能力、计算能力、分析能力依赖于作为其基础的SAS语言。SAS语 言是一个专用的数据管理与分析语言,它的数据管理功能类似于数据库语言(如FoxPro), 但又添加了一般高级程序设计语言的许多成分(如分支、循环、数组),以及专用于数据管 理、统计计算的函数。SAS系统的数据管理、报表、图形、统计分析等功能都可以用SAS语言 程序来调用,只要指定要完成的任务就可以由SAS系统按照预先设计好的程序去进行,所以SAS 语言和FoxPro等一样是一种第四代语言。

本章简单介绍SAS语言的基本成分与规则,SAS语言如何用来管理数据,SAS语言作为一个 统计计算语言的用法,以及SAS过程使用的初步知识。

SAS语言构成 SAS语句

SAS语言程序由数据步和过程步组成。数据步用来生成数据集、计算、整理数据,过 程步用来对数据进行分析、报告。SAS语言的基本单位是语句,每个SAS语句一般由一个关键 字(如DATA,PROC,INPUT,CARDS,BY)开头,包含SAS名字、特殊字符、运算符等,以分号 结束。

SAS关键字是用于SAS语句开头的特殊单词,SAS语句除了赋值、累加、注释、空语句以外 都以关键字开头。SAS名字在SAS程序中标识各种SAS成分,如变量、数据集、数据库,等等。SAS 名字由1到8个字母、数字、下划线组成,第一个字符必须是字母或下划线。SAS关键字和SAS 名字都不分大小写。

SAS表达式

SAS数据步程序中的计算用表达式完成。表达式把常量、变量、函数调用用运算符、 括号连接起来得到一个计算结果。

SAS常量主要有数值型、字符型两种,并且还提供了用于表达日期、时间的数据类型。例 如

数值型:12,-7.5,2.5E-10

字符型:'Beijing',"Li Ming","李明"

日期型:'13JUL1998'd

时间型:'14:20't

日期时间型:'13JUL1998:14:20:32'dt

数值型常数可以用整数、定点实数、科学计数法实数表示。字符型常数为两边用单撇号或 两边用双撇号包围的若干字符。日期型常数是在表示日期的字符串后加一个字母d(大小写均 可),中间没有空格。时间型常数是在表示时间的字符串后加一个字母t。日期时间型常数在 表示日期时间的字符串后加字母dt。

因为SAS是一种数据处理语言,而实际数据中经常会遇到缺失值,比如没有观测到数 值,被访问人不肯回答,等等。SAS中用一个单独的小数点来表示缺失值常量。

SAS变量的基本类型有两种:数值型和字符型。日期、时间等变量存为数值型。SAS的 数值型变量可以存储任意整数、定点实数、浮点实数,一般不关心其区别。数值型变量在数 据集中的存贮一般使用8个字节。SAS的字符型变量缺省的长度是8个字符,但是如果在INPUT 语句中输入字符型变量时指定了长度则不受此限制。可以用LENGTH语句直接指定变量长度,LENGTH 语句一般应出现在变量定义之前,格式为:

LENGTH 变量名 $ 长度;

例如

LENGTH name $ 20;

SAS运算符包括算术、比较、逻辑等运算符。

算术运算符为 + - * / **,运算优先级按通常的优先规则。

比较运算符用于比较常量、变量的值大小、相等,包括

= ^= > < >= = 1000) AND (salary < 2000) 表示工资收入在1000-2000之间 (不含2000)

(age = 1000) AND (salary < 2000)) 表示工资收入 不在1000-2000之间

复杂的逻辑表达式最好用括号表示其运算优先级以免误记优先规则并可利于阅读程序。

其它的运算符还有用于连接两个字符串的||(两个连续的|号),用于取两个运算值 中较大一个的(比如35结果为5),用于取两个运算值中较小一个的>< (比如3>0 THEN PUT 'X为正数';

有时我们在条件成立时需要进行的操作无法用一个语句完成,这时可以使用SAS提供 的 复合语句功能:只要把若干个语句用“DO;”语句和“END;”语句包围起来,就可以 把它们看作是一个语句,就可以用在需要指定一个语句的地方。比如,当X为正数时不仅显示X 为正数,而且将其加倍并显示值,可以用如下带有复合语句的IF结构:

IF x>0 THEN DO; PUT ' X为正数'; x = 2*x; PUT x=; END;

以上的IF结构的用法只规定了条件成立时的操作,如果同时需要规定条件不成立时进 行什么操作,使用带有ELSE字句的IF结构:

IF 条件 THEN 语句; ELSE 语句;

其中“语句”均可以是复合语句。 例如,当X为非负时将X加倍,为负时将X取绝对值,用如 下程序:

IF x>=0 THEN x=2*x; ELSE x = -x;

注意SAS的分支结构的写法与其它语言有些不同,它不用ENDIF结束。

SAS的IF结构允许嵌套,但SAS不提供IF-ELSEIF-ELSE的多分支结构。SAS的SELECT 结构提供了更为灵活的多分支结构,可以实现比其它语言的IF-ELSEIF-ELSE结构更强的功 能。SELECT结构有两种基本用法,第一种为:

SELECT (选择表达式); WHEN(值列表) 语句; WHEN(值列表) 语句; …… OTHERWISE 语句; END;

其中“选择表达式”是一个取数值、字符型值的变量或表达式, “值列表”为一项或者若干项,多项之间逗号分开,每项可以是一个与选择表达式相同 取值类型的表达式。 “语句”可以是单个语句或复合语句。执行SELECT结构时,先计算出选择表达式和值列 表中的所有值,然后把选择表达式值由前向后与值列表中的值相比,发现相等值则执行对应 的语句,然后退出SELECT结构(不再查看后面的值列表)。如果选择表达式的值不等于任何 值列表中的值则执行OTHERWISE对应的语句,这种情况下没有OTHERWISE语句会出错。例如:

SELECT(month); WHEN('Feb', 'Mar', 'Apr') put '春天'; WHEN('May', 'Jun', 'Jul') put '夏天'; OTHERWISE put '秋天或冬天'; END; 根据MONTH值不同显示不同的季节。

SELECT语句的另一种形式为:

SELECT; WHEN(条件) 语句; WHEN(条件) 语句; …… OTHERWISE 语句; END;

这种SELECT语句没有选择表达式,而是在每一个WHEN语句指定一个条件(逻辑表达式), 执行第一个满足条件的WHEN后的语句。如果所有条件都不满足则执行OTHERWISE后的语句。例 如:

SELECT; WHEN(age0,得到参数为alpha的伽马分布 。设X=RANGAM(seed, alpha),则Y=beta*X是形状参数为alpha,尺度参数为beta的GAMMA分布 随机数。如果alpha是整数,则Y=2*X是自由度为2*alpha的卡方分布随机数。

如果alpha是正整数,则Y=beta*X是Erlang分布随机数,为alpha个独立的均值为beta的指 数分布变量的和。

如果Y1=RANGAM(seed,alpha),Y2=RANGAM(seed,beta),在Y=Y1/(Y1+Y2)是参数为(alpha,beta )的贝塔分布随机数。

5.三角分布随机数

RANTRI(seed,h),seed为任意数值常数,0=90 and chinese>=100; run;

注意子集IF语句不同于我们前面所讲的分支语句,它没有THEN部分,只有条件,用于取出 满足条件的行子集。

用SET和OUTPUT语句拆分数据集

有时我们需要根据某一分类原则把数据行分别存放到不同的数据集。比如,我们希望 把数据集C9501中的所有男生的观测放到数据集C9501M中,把所有女生的观测放到C9501F中, 可以使用如下程序:

data c9501m c9501f; set c9501; select(sex); when('男') output c9501m; when('女') output c9501f; otherwise put sex= '有错'; end; drop sex; run; proc print data=c9501m;run; proc print data=c9501f;run;

这个程序中有两个地方需要注意:在DATA语句中,我们指定了两个数据集名,这表示要生 成两个数据集。程序中用SET语句引入了一个数据集,这个数据集的观测如何分配到两个结果 数据集中呢?关键在于OUTPUT语句。OUTPUT语句是一个可执行语句,它使得当前观测被写到 语句指定的数据集中。这样,我们根据SELECT的结果把不同性别分别放到了两个不同数据集 中。

OUTPUT语句还可以用来强行写入数据集而不必象我们在数据步流程图中说明的那样等 到数据步最后一个语句完成。数据步中有了OUTPUT语句后数据步流程中不再有自动写入观测 的操作,而只能由OUTPUT语句指定输出。不指定数据集名的OUTPUT语句输出到第一个结果数 据集。比如下面的程序生成一个包含1到10的及其平方的有10个观测的数据集:

data sq; do i=1 to 10; j=i*i; output; end; run; proc print;run;

如果删去上面的OUTPUT语句则结果数据集中只有i=11,j=100的一个观测。

数据集的纵向合并

几个结构相同的数据集可以上下地连接到一起。比如,我们有四个班的学生情况的数 据集Class1-Class4,每个数据集包含一个班学生的学号、姓名、性别信息,我们希望把这些 数据集合并为一个大数据集,可以用如下代码:

data classes; set class1 class2 class3 class4; run;

可见,要把若干个结构相同的数据集合并为一个数据集,只要在DATA语句中指定要生成的 大数据集的名字,然后在数据步中使用SET语句并在SET语句中依次列出各小数据集。

有时我们需要在合并数据集时加入一个变量来指示每一个观测原来来自哪一个小数据 集,这可以在SET语句的每一个数据集名后面加一个括号,里面写上IN=变量名,变量名所给 的变量取1表示观测来自此数据集,取0表示观测非来自此数据集。例如,在2.3.5中我们把C9501 数据集按男、女拆分成了C9501M和C9501F两个数据集并抛弃了性别变量,就可以用如下程序 连接两个数据集并恢复性别信息:

data new; set c9501m(in=male) c9501f(in=female); if male=1 then sex='男'; if female=1 then sex='女'; run;

在数据步中,如果观测来自C9501M,则变量MALE值为1,如果观测来自C9501F则变量FEMALE 值为1,可以使用这两个变量的值定义新变量SEX。用数据集选项的IN=指定的变量不能直接进 入结果数据集而只能用于数据步程序中。

数据集的横向合并

两个(或多个)数据集如果包含了同样的一些观测的不同属性(变量),比如,数据 集C9501U包含学生的姓名、性别,数据集C9501V包含学生的数学成绩,数据集C9501W包含学 生的语文成绩,且各数据集的观测是按顺序一一对应的,就可以用如下带有MERGE语句的数据 步把它们左右横向合并到一个数据集NEW:

data new; merge c9501u c9501v c9501w; run;

这样虽然可以横向合并数据集,但是如果各数据集的观测顺序并不一样,就会把不同 人的成绩合并到一起。所以横向合并一般应该采用按关键字合并的办法,即先把每个数据集 按照相同的、能唯一区分各观测的一个(或几个)变量排序,然后用BY语句和MERGE语句联合 使用,这样即使原来观测顺序不一致也可以保证横向合并的结果没有错。下例先把C9501数据 集横向拆分为包含姓名、性别的数据集C9501X和包含姓名、数学成绩、语文成绩的数据集C9501Y ,然后按关键字横向合并:

data c9501x; set c9501; keep name sex; run; data c9501y; set c9501; keep name math chinese; run;   proc sort data=c9501x; by name; run; proc sort data=c9501y; by name; run; data new; merge c9501x c9501y; by name; run; proc print;run;

其中的PROC SORT是排序过程,用来把数据集按照某个变量的次序排序(这里是按变量NAME 的次序排列,用BY语句指定排序的变量名)。

用UPDATE语句更新数据集

如果我们发现数据集中的某些数据值有错误或者现在的值已经改变了,我们可以从更 正了的原始数据重新生成数据集,或者使用更有效的方法,即建立一个只包含新数据值的数 据集,用此数据集修改原数据集。使用如下的DATA步中可以实现数据集的更新:

DATA 新数据集名; UPDATE 原数据集 更新用数据集; BY 关键变量; RUN;

例如,比如我们发现数据集C9501中王思明的语文成绩实际应该是91分,张红艺性别应为 男,可以先生成如下的只包含更正数据值的数据集,不需要改的观测不列入,不需要改的变 量不列入或取缺失值:

data upd; input name $ sex $ chinese; cards; 张红艺 男 . 王思明 . 91 ; run;

然后,把原数据集C9501和更新用数据集UPD均按姓名(NAME)排序:

proc sort data=c9501; by name; run; proc sort data=upd; by name; run;

最后用UPDATE和BY更新得到新数据集NEW,其中王思明的语文成绩改成了91分,张红艺性 别改成了男。

data new; update c9501 upd; by name; run; proc print;run; 但是,这个新数据集中有一个错误:王思明的语文成绩修改以后他的平均分也应作相应改动。 所以此例应改为: data new; update c9501 upd(in=in_upd); if in_upd=1 then avg = math*0.5 + chinese/120*100*0.5; by name; run; proc print;run; 用PROC SQL管理数据

SAS系统首先是一个数据管理系统,因此它除了可以用SAS语言程序管理SAS数据库、 数据集外,还提供了其它大型数据库管理系统(如Oracle、Sybase)通用的SQL语言功能。在SAS 系统中SQL语言实现在SQL过程中。SAS的SQL过程可以从一个或多个表中查询信息,生成表, 向表中插入行,更新表的内容,对表进行纵向合并、横向连接等等。另外,PROC SQL还可以 直接连接外部数据库,我们已经在2.1.3作了介绍。SQL语言可以实现极其复杂的数据管理功 能,在这里我们只对它的查询功能作简单介绍,感兴趣的读者可以自己阅读一些数据库管理 方面的书籍。

用PROC SQL作查询的最简单的用法如下:

PROC SQL; SELECT 第一项,第二项,…,第n项 FROM 数据集 WHERE 观测选择条件; RUN;

其中SELECT是一个语句,FROM和WHERE叫做子句,注意语句是在最后结尾的,中间没有分 号。SELECT子句中指定的各项一般为变量名,中间用逗号分隔(注意不是用空格分隔)。FROM 子句指定要从哪个数据集查询。WHERE子句指定选择观测的条件。所以,SELECT语句可以很方 便地从一个表查询一个子集,并可以自动输出到输出窗口而不需再使用PROC PRINT。例如, 下面的程序显示语文成绩在100分以上(包含)的学生的姓名和数学成绩:

proc sql; select name, math from c9501 where chinese>=100; run;

结果显示

NAME MATH -------------------- 张红艺 89 张聪 98 刘颍 80

在SELECT语句中还可以加入ORDER BY子句,可以为查询结果排序。比如,下程序

proc sql; select name, math from c9501 where chinese>=100 order by math desc; run;

结果为

NAME MATH -------------------- 张聪 98 张红艺 89 刘颍 80

SELECT的强大查询功能还表现在它可以从几个表联合查询。比如,考虑2.3.7中的C9501X 和C9501Y,我们要从这两个数据集查询与从C9501一个数据集同样的结果,可以用此程序:

proc sql; select c9501x.name, math from c9501x, c9501y where c9501x.name=c9501y.name and chinese>=100 order by math desc; run;

其中连接两个数据集的办法是在WHERE子句指定C9501X.NAME=C9501Y.NAME这样的连接条件 。在SELECT中指定变量时如果有两个数据集中共有的变量要用C9501X.NAME这样的带有表名( 数据集名)的形式。

连接的两个表有时是同一个表。比如,我们有几个学生的姓名和生日,希望找出那些 有相同生日的人。可以用如下的SQL过程:

title '找出生日相同的人'; data class; input name $ 1-8 birth yymmdd10.; format birth yymmdd10.; label name='姓名' birth='生日'; cards; 李明 78-6-1 王思明 78-5-19 张聪 78-6-1 刘颖 78-10-18 张红艺 78-5-19 ; proc sql; select name, birth from class a where birth in select birth from class b where b.name ^= a.name order by a.birth; run;

结果如下:

找出生日相同的人 21   姓名 生日 -------------------- 王思明 1978-05-19 张红艺 1978-05-19 张聪 1978-06-01 李明 1978-06-01

如果我们还希望把查询的结果存入一个数据集,可以在上面的第一个SELECT语句前面加上CREATE TABLE 表名 AS:

proc sql; CREATE TABLE bsame AS select name, birth from class a ………………… run; proc print data=bsame label; id name; by birth; run;

结果如下:

找出生日相同的人 22   ------------------------------ 生日=1978-05-19 -------------------------------   姓名   王思明 张红艺     ------------------------------ 生日=1978-06-01 -------------------------------   姓名   张聪 李明

如果不用SQL过程想得到同样的结果,可以使用如下数据步和过程步:

proc freq data=class noprint; tables birth / out=bfreq; run; proc sort data=class; by birth; proc sort data=bfreq; by birth; data bsame; merge class bfreq; by birth; if count>1; run; proc print data=bsame label noobs; var name; by birth; run; 练习 用SAS数据步列出10000以下的素数,写出程序。 生成t分布的双侧分位数表。水平取0.001,0.002,0.005,0.01,0.02,0.05,0.10 ,0.20,自由度取1-100,分位数精确到小数点后3位。表格应为行、列对齐的形式,并有列 标题。写出生成这样的表格并存放到一个文本文件中的SAS程序。 写出计算从自己生日到2000年初经过的天数的程序。 下表为某邮购服务部的部分顾客记录:

姓名

性别

地区

日期

金额

章文

华东

1996-3-20

1099

王国铭

华东

1996-5-19

39

童子敏

华北

1996-1-5

986

刘念新

东北

1997-10-1

3581

李思今

华北

1997-4-4

659

关昭

东北

1996-11-5

358

赵霞

东北

1998-9-6

2010

(1)用数据步把此数据输入到SAS数据集;

(2)用程序找出男性顾客购买金额超过1000的哪些人;

(3)把数据拆分为包含姓名、性别、地区的一个数据集和包含姓名、日期、金额的一个 数据集;

(4)用MERGE和BY合并上一步拆开的两个数据集。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3